@fieldwangai/agentflow 0.1.115 → 0.1.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/ui-server.mjs +310 -23
- package/builtin/web-ui/dist/assets/index-BKLuP3ZS.css +1 -0
- package/builtin/web-ui/dist/assets/index-DQJFzY30.js +350 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-CuRtjhm3.css +0 -1
- package/builtin/web-ui/dist/assets/index-sFdIyhv_.js +0 -349
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -3133,6 +3133,48 @@ function hydrateWorkspaceGraphForRuntime(workspaceRoot, scoped = {}, graph = {},
|
|
|
3133
3133
|
return hydrateWorkspaceSlotMetaFromDefinitions(workspaceRoot, scoped, withMarketplaceRuntime, userCtx);
|
|
3134
3134
|
}
|
|
3135
3135
|
|
|
3136
|
+
function adminWorkspaceOwnerSummary(ownerId = "") {
|
|
3137
|
+
const id = String(ownerId || "").trim();
|
|
3138
|
+
if (!id) return null;
|
|
3139
|
+
const users = readAuthUsers();
|
|
3140
|
+
const knownUserIds = new Set([
|
|
3141
|
+
...Object.keys(users || {}),
|
|
3142
|
+
...listAgentflowUserIds(),
|
|
3143
|
+
].map((value) => String(value || "").trim()).filter(Boolean));
|
|
3144
|
+
if (!knownUserIds.has(id)) return null;
|
|
3145
|
+
return {
|
|
3146
|
+
userId: id,
|
|
3147
|
+
username: String(users?.[id]?.username || id),
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
function adminWorkspaceRequestedUserContext(userCtx = {}) {
|
|
3152
|
+
const ownerId = String(userCtx.adminOwnerId || "").trim();
|
|
3153
|
+
if (!ownerId) return { userCtx };
|
|
3154
|
+
if (userCtx.isAdmin !== true) {
|
|
3155
|
+
return { error: "Admin permission required", status: 403 };
|
|
3156
|
+
}
|
|
3157
|
+
const owner = adminWorkspaceOwnerSummary(ownerId);
|
|
3158
|
+
if (!owner) return { error: "Workspace owner not found", status: 404 };
|
|
3159
|
+
return {
|
|
3160
|
+
owner,
|
|
3161
|
+
userCtx: {
|
|
3162
|
+
...userCtx,
|
|
3163
|
+
userId: owner.userId,
|
|
3164
|
+
adminOwnerId: "",
|
|
3165
|
+
},
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
function workspaceScopedUserContext(scoped = {}, userCtx = {}) {
|
|
3170
|
+
if (!scoped.adminReadonly || !scoped.ownerUserId) return userCtx;
|
|
3171
|
+
return {
|
|
3172
|
+
...userCtx,
|
|
3173
|
+
userId: scoped.ownerUserId,
|
|
3174
|
+
adminOwnerId: "",
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3136
3178
|
function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {}) {
|
|
3137
3179
|
const flowId = params.flowId != null ? String(params.flowId).trim() : "";
|
|
3138
3180
|
if (!flowId) return { root: path.resolve(workspaceRoot), flowId: "", flowSource: "", archived: false };
|
|
@@ -3143,6 +3185,46 @@ function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {}) {
|
|
|
3143
3185
|
if (!isValidFlowSourceRead(flowSource)) {
|
|
3144
3186
|
return { root: "", error: "Invalid flowSource" };
|
|
3145
3187
|
}
|
|
3188
|
+
const adminOwnerId = String(params.adminOwnerId || opts.adminOwnerId || "").trim();
|
|
3189
|
+
if (adminOwnerId) {
|
|
3190
|
+
if (opts.isAdmin !== true) {
|
|
3191
|
+
return { root: "", error: "Admin permission required", status: 403 };
|
|
3192
|
+
}
|
|
3193
|
+
if (flowSource !== "user") {
|
|
3194
|
+
return { root: "", error: "Admin read-only review only supports user projects", status: 400 };
|
|
3195
|
+
}
|
|
3196
|
+
const owner = adminWorkspaceOwnerSummary(adminOwnerId);
|
|
3197
|
+
if (!owner) {
|
|
3198
|
+
return { root: "", error: "Workspace owner not found", status: 404 };
|
|
3199
|
+
}
|
|
3200
|
+
const targetFlow = listFlowsJson(workspaceRoot, { userId: owner.userId })
|
|
3201
|
+
.find((flow) => (
|
|
3202
|
+
flow.id === flowId
|
|
3203
|
+
&& (flow.source || "user") === "user"
|
|
3204
|
+
&& Boolean(flow.archived) === archived
|
|
3205
|
+
));
|
|
3206
|
+
if (!targetFlow?.path) {
|
|
3207
|
+
return { root: "", error: "Pipeline workspace not found", status: 404 };
|
|
3208
|
+
}
|
|
3209
|
+
return {
|
|
3210
|
+
root: path.resolve(targetFlow.path),
|
|
3211
|
+
flowId,
|
|
3212
|
+
flowSource: "user",
|
|
3213
|
+
requestedFlowSource: flowSource,
|
|
3214
|
+
workspaceId: "",
|
|
3215
|
+
archived,
|
|
3216
|
+
collaboration: null,
|
|
3217
|
+
collaborationAccess: {
|
|
3218
|
+
allowed: true,
|
|
3219
|
+
writable: false,
|
|
3220
|
+
runnable: false,
|
|
3221
|
+
role: "admin-viewer",
|
|
3222
|
+
},
|
|
3223
|
+
adminReadonly: true,
|
|
3224
|
+
ownerUserId: owner.userId,
|
|
3225
|
+
ownerUsername: owner.username,
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3146
3228
|
const workspaceId = String(params.workspaceId || "").trim();
|
|
3147
3229
|
let collaboration = getWorkspaceCollaborationForProject({
|
|
3148
3230
|
workspaceId,
|
|
@@ -3255,9 +3337,11 @@ function prdWorkflowShareLinkSummary(record, shareToken, publicBaseUrl, userId =
|
|
|
3255
3337
|
workflowShare: token,
|
|
3256
3338
|
});
|
|
3257
3339
|
const base = String(publicBaseUrl || "").replace(/\/+$/, "");
|
|
3340
|
+
const shortUrl = `${base}/w/${encodeURIComponent(token)}`;
|
|
3258
3341
|
return {
|
|
3259
3342
|
tapdId: String(record.tapdId || ""),
|
|
3260
3343
|
url: `${base}/workspace?${query.toString()}`,
|
|
3344
|
+
shortUrl,
|
|
3261
3345
|
active: true,
|
|
3262
3346
|
readOnly: true,
|
|
3263
3347
|
createdAt: record.shareCreatedAt || "",
|
|
@@ -7472,6 +7556,17 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
|
|
|
7472
7556
|
const flowId = String(params.flowId || "").trim();
|
|
7473
7557
|
const flowSource = String(params.flowSource || "user").trim() || "user";
|
|
7474
7558
|
const archived = params.archived === true || params.archived === "1" || params.flowArchived === true;
|
|
7559
|
+
const adminOwnerId = String(params.adminOwnerId || userCtx.adminOwnerId || "").trim();
|
|
7560
|
+
if (adminOwnerId && userCtx.isAdmin !== true) {
|
|
7561
|
+
return { error: "Admin permission required", status: 403 };
|
|
7562
|
+
}
|
|
7563
|
+
const adminOwner = adminOwnerId ? adminWorkspaceOwnerSummary(adminOwnerId) : null;
|
|
7564
|
+
if (adminOwnerId && !adminOwner) {
|
|
7565
|
+
return { error: "Workspace owner not found", status: 404 };
|
|
7566
|
+
}
|
|
7567
|
+
if (adminOwner && capability !== "read") {
|
|
7568
|
+
return { error: "Admin Workspace review is read-only", status: 403 };
|
|
7569
|
+
}
|
|
7475
7570
|
const shareToken = String(params.workflowShare || params.workflow_share || "").trim();
|
|
7476
7571
|
const linkCollaboration = shareToken ? getPrdWorkflowCollaborationByShareToken(shareToken) : null;
|
|
7477
7572
|
if (shareToken && (!linkCollaboration || linkCollaboration.tapdId !== tapdId)) {
|
|
@@ -7480,8 +7575,10 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
|
|
|
7480
7575
|
const memberCollaboration = tapdId
|
|
7481
7576
|
? getPrdWorkflowCollaborationForUser(tapdId, userCtx?.userId)
|
|
7482
7577
|
: null;
|
|
7483
|
-
const collaboration = linkCollaboration || memberCollaboration;
|
|
7484
|
-
const access =
|
|
7578
|
+
const collaboration = adminOwner ? null : (linkCollaboration || memberCollaboration);
|
|
7579
|
+
const access = adminOwner
|
|
7580
|
+
? { allowed: true, writable: false, role: "admin-viewer", via: "admin-review" }
|
|
7581
|
+
: linkCollaboration
|
|
7485
7582
|
? { allowed: true, writable: false, role: "viewer", via: "share-link" }
|
|
7486
7583
|
: prdWorkflowCollaborationAccess(collaboration, userCtx?.userId);
|
|
7487
7584
|
if (collaboration && !access.allowed) {
|
|
@@ -7490,7 +7587,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
|
|
|
7490
7587
|
if (capability === "write" && (linkCollaboration || (collaboration && !access.writable))) {
|
|
7491
7588
|
return { error: "PRD Workflow collaboration edit permission denied", status: 403 };
|
|
7492
7589
|
}
|
|
7493
|
-
const ownerId = String(collaboration?.ownerId || userCtx?.userId || "").trim();
|
|
7590
|
+
const ownerId = String(adminOwner?.userId || collaboration?.ownerId || userCtx?.userId || "").trim();
|
|
7494
7591
|
const stateRoot = path.resolve(getAgentflowUserDataRoot(ownerId));
|
|
7495
7592
|
let executionRoot = path.resolve(workspaceRoot);
|
|
7496
7593
|
if (flowId) {
|
|
@@ -7498,6 +7595,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
|
|
|
7498
7595
|
flowId,
|
|
7499
7596
|
flowSource,
|
|
7500
7597
|
workspaceId: params.workspaceId || "",
|
|
7598
|
+
adminOwnerId,
|
|
7501
7599
|
archived,
|
|
7502
7600
|
}, userCtx);
|
|
7503
7601
|
if (projectScope.error) {
|
|
@@ -7516,6 +7614,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
|
|
|
7516
7614
|
collaborationAccess: access,
|
|
7517
7615
|
shareToken,
|
|
7518
7616
|
sharedByLink: Boolean(linkCollaboration),
|
|
7617
|
+
adminReadonly: Boolean(adminOwner),
|
|
7519
7618
|
flowId,
|
|
7520
7619
|
flowSource,
|
|
7521
7620
|
archived,
|
|
@@ -7526,7 +7625,8 @@ function prdWorkflowKey(userCtx = {}, flowSource = "user", flowId = "", tapdId =
|
|
|
7526
7625
|
const id = String(tapdId || "").trim();
|
|
7527
7626
|
const collaboration = getPrdWorkflowCollaborationByShareToken(shareToken)
|
|
7528
7627
|
|| getPrdWorkflowCollaborationForUser(id, userCtx?.userId);
|
|
7529
|
-
const
|
|
7628
|
+
const adminOwnerId = userCtx?.isAdmin === true ? String(userCtx?.adminOwnerId || "").trim() : "";
|
|
7629
|
+
const actorScope = `user:${String(collaboration?.ownerId || adminOwnerId || userCtx?.userId || "")}`;
|
|
7530
7630
|
return [actorScope, id].join("\t");
|
|
7531
7631
|
}
|
|
7532
7632
|
|
|
@@ -7678,6 +7778,32 @@ function prdWorkflowReviewIdFromRequest(tapdId, payload = {}, durability = "temp
|
|
|
7678
7778
|
return prdWorkflowSafeStateId(["review", tapdId, stageHead, digest].filter(Boolean).join("-")).slice(0, 64);
|
|
7679
7779
|
}
|
|
7680
7780
|
|
|
7781
|
+
function prdWorkflowReviewArtifactKey(tapdId, payload = {}, durability = "temporary") {
|
|
7782
|
+
const explicit = String(payload.artifactKey || payload.artifact_key || "").trim();
|
|
7783
|
+
if (explicit) return explicit.slice(0, 500);
|
|
7784
|
+
const issueKey = prdWorkflowSafeStateId(
|
|
7785
|
+
payload.issueKey || payload.issue_key || payload.issue || "global",
|
|
7786
|
+
).toLowerCase();
|
|
7787
|
+
const platform = prdWorkflowSafeStateId(payload.platform || "all").toLowerCase();
|
|
7788
|
+
const stage = prdWorkflowSafeStateId(
|
|
7789
|
+
payload.stage
|
|
7790
|
+
|| payload.stageKey
|
|
7791
|
+
|| payload.stage_key
|
|
7792
|
+
|| payload.action
|
|
7793
|
+
|| payload.actionId
|
|
7794
|
+
|| payload.action_id
|
|
7795
|
+
|| "review",
|
|
7796
|
+
).toLowerCase();
|
|
7797
|
+
return [
|
|
7798
|
+
"prd-review",
|
|
7799
|
+
prdWorkflowSafeStateId(tapdId).toLowerCase(),
|
|
7800
|
+
issueKey,
|
|
7801
|
+
platform,
|
|
7802
|
+
stage,
|
|
7803
|
+
String(durability || "temporary").trim().toLowerCase() || "temporary",
|
|
7804
|
+
].join(":").slice(0, 500);
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7681
7807
|
function prdWorkflowStatePath(scopedRoot, tapdId) {
|
|
7682
7808
|
const rootDir = scopedRoot || process.cwd();
|
|
7683
7809
|
return path.join(rootDir, ".workspace", "prd-flow", "workflow-state", `${prdWorkflowSafeStateId(tapdId)}.json`);
|
|
@@ -9131,18 +9257,27 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
|
|
|
9131
9257
|
|
|
9132
9258
|
function prdWorkflowMergeRuntimeEventArrays(left, right) {
|
|
9133
9259
|
const out = [];
|
|
9134
|
-
const seen = new
|
|
9260
|
+
const seen = new Map();
|
|
9135
9261
|
for (const value of [...(Array.isArray(left) ? left : []), ...(Array.isArray(right) ? right : [])]) {
|
|
9136
9262
|
if (value == null) continue;
|
|
9137
|
-
|
|
9263
|
+
const explicitKey = value && typeof value === "object" && !Array.isArray(value)
|
|
9264
|
+
? String(value.key || value.artifactKey || value.artifact_key || "").trim()
|
|
9265
|
+
: "";
|
|
9266
|
+
let key = explicitKey ? `key:${explicitKey}` : "";
|
|
9138
9267
|
try {
|
|
9139
|
-
key = typeof value === "string" ? value : JSON.stringify(value);
|
|
9268
|
+
if (!key) key = typeof value === "string" ? value : JSON.stringify(value);
|
|
9140
9269
|
} catch {
|
|
9141
9270
|
key = String(value);
|
|
9142
9271
|
}
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9272
|
+
const index = seen.get(key);
|
|
9273
|
+
if (index == null) {
|
|
9274
|
+
seen.set(key, out.length);
|
|
9275
|
+
out.push(value);
|
|
9276
|
+
continue;
|
|
9277
|
+
}
|
|
9278
|
+
if (explicitKey) {
|
|
9279
|
+
out[index] = value;
|
|
9280
|
+
}
|
|
9146
9281
|
}
|
|
9147
9282
|
return out;
|
|
9148
9283
|
}
|
|
@@ -10958,7 +11093,45 @@ export function startUiServer({
|
|
|
10958
11093
|
}
|
|
10959
11094
|
|
|
10960
11095
|
const authUser = getAuthUserFromRequest(req);
|
|
10961
|
-
const userCtx = authUser ? {
|
|
11096
|
+
const userCtx = authUser ? {
|
|
11097
|
+
userId: authUser.userId,
|
|
11098
|
+
isAdmin: Boolean(authUser.isAdmin),
|
|
11099
|
+
adminOwnerId: String(url.searchParams.get("adminOwnerId") || "").trim(),
|
|
11100
|
+
} : {};
|
|
11101
|
+
if (req.method === "GET" && url.pathname.startsWith("/w/")) {
|
|
11102
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
11103
|
+
if (parts.length !== 2) {
|
|
11104
|
+
res.writeHead(404);
|
|
11105
|
+
res.end("Not found");
|
|
11106
|
+
return;
|
|
11107
|
+
}
|
|
11108
|
+
let shareToken = "";
|
|
11109
|
+
try {
|
|
11110
|
+
shareToken = decodeURIComponent(parts[1] || "");
|
|
11111
|
+
} catch {
|
|
11112
|
+
res.writeHead(404);
|
|
11113
|
+
res.end("Not found");
|
|
11114
|
+
return;
|
|
11115
|
+
}
|
|
11116
|
+
const record = getPrdWorkflowCollaborationByShareToken(shareToken);
|
|
11117
|
+
if (!record) {
|
|
11118
|
+
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
11119
|
+
res.end("Workflow share link is invalid or has been revoked");
|
|
11120
|
+
return;
|
|
11121
|
+
}
|
|
11122
|
+
const query = new URLSearchParams({
|
|
11123
|
+
view: "workflow",
|
|
11124
|
+
tapdId: String(record.tapdId || ""),
|
|
11125
|
+
workflowShare: shareToken,
|
|
11126
|
+
});
|
|
11127
|
+
res.writeHead(302, {
|
|
11128
|
+
Location: `/workspace?${query.toString()}`,
|
|
11129
|
+
"Cache-Control": "no-store",
|
|
11130
|
+
"Referrer-Policy": "no-referrer",
|
|
11131
|
+
});
|
|
11132
|
+
res.end();
|
|
11133
|
+
return;
|
|
11134
|
+
}
|
|
10962
11135
|
if (req.method === "GET" && url.pathname === "/api/auth/session-token") {
|
|
10963
11136
|
if (!authUser?.userId) {
|
|
10964
11137
|
json(res, 401, { error: "Unauthorized" });
|
|
@@ -11281,7 +11454,7 @@ export function startUiServer({
|
|
|
11281
11454
|
json(res, 200, {
|
|
11282
11455
|
ok: true,
|
|
11283
11456
|
snapshot,
|
|
11284
|
-
...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
|
|
11457
|
+
...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
|
|
11285
11458
|
});
|
|
11286
11459
|
} catch (e) {
|
|
11287
11460
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
@@ -11470,7 +11643,7 @@ export function startUiServer({
|
|
|
11470
11643
|
json(res, 200, {
|
|
11471
11644
|
ok: true,
|
|
11472
11645
|
snapshot: withDiagnostic,
|
|
11473
|
-
...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
|
|
11646
|
+
...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
|
|
11474
11647
|
});
|
|
11475
11648
|
} catch (e) {
|
|
11476
11649
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
@@ -12195,10 +12368,15 @@ export function startUiServer({
|
|
|
12195
12368
|
const shortUrl = shortLink?.shortUrl || "";
|
|
12196
12369
|
const displayUrl = shortUrl || reviewUrl;
|
|
12197
12370
|
const durability = review.durability || "temporary";
|
|
12371
|
+
const artifactKey = prdWorkflowReviewArtifactKey(tapdId, payload, durability);
|
|
12198
12372
|
const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
|
|
12199
12373
|
? review.source
|
|
12200
12374
|
: { kind: durability === "durable" ? "ai-doc" : "local-draft", durability };
|
|
12375
|
+
const idempotencyKey = String(
|
|
12376
|
+
payload.idempotencyKey || payload.idempotency_key || "",
|
|
12377
|
+
).trim();
|
|
12201
12378
|
const artifact = {
|
|
12379
|
+
key: artifactKey,
|
|
12202
12380
|
label: payload.artifactLabel || "Markdown Review",
|
|
12203
12381
|
kind: durability === "temporary" ? "temporary-review" : "review",
|
|
12204
12382
|
persistence: "runtime",
|
|
@@ -12222,12 +12400,15 @@ export function startUiServer({
|
|
|
12222
12400
|
detail: "已生成临时 Markdown review 链接",
|
|
12223
12401
|
status: "current",
|
|
12224
12402
|
issueKey: payload.issueKey || payload.issue_key || "",
|
|
12403
|
+
idempotencyKey,
|
|
12225
12404
|
durability,
|
|
12226
12405
|
sourceArtifact: reviewSource,
|
|
12227
12406
|
expiresAt: review.expiresAt || "",
|
|
12228
12407
|
artifacts: [artifact],
|
|
12229
12408
|
links: [{
|
|
12409
|
+
key: artifactKey,
|
|
12230
12410
|
label: "Markdown Review",
|
|
12411
|
+
kind: artifact.kind,
|
|
12231
12412
|
url: displayUrl,
|
|
12232
12413
|
canonicalUrl: reviewUrl,
|
|
12233
12414
|
shortUrl,
|
|
@@ -12647,6 +12828,36 @@ export function startUiServer({
|
|
|
12647
12828
|
return;
|
|
12648
12829
|
}
|
|
12649
12830
|
|
|
12831
|
+
if (req.method === "GET" && url.pathname === "/api/admin/user-workspaces") {
|
|
12832
|
+
if (!authUser?.isAdmin) {
|
|
12833
|
+
json(res, 403, { error: "Admin permission required" });
|
|
12834
|
+
return;
|
|
12835
|
+
}
|
|
12836
|
+
const owner = adminWorkspaceOwnerSummary(url.searchParams.get("userId") || "");
|
|
12837
|
+
if (!owner) {
|
|
12838
|
+
json(res, 404, { error: "Workspace owner not found" });
|
|
12839
|
+
return;
|
|
12840
|
+
}
|
|
12841
|
+
try {
|
|
12842
|
+
const workspaces = listFlowsJson(root, { userId: owner.userId })
|
|
12843
|
+
.filter((flow) => (flow.source || "user") === "user")
|
|
12844
|
+
.map((flow) => ({
|
|
12845
|
+
id: String(flow.id || ""),
|
|
12846
|
+
source: "user",
|
|
12847
|
+
archived: flow.archived === true,
|
|
12848
|
+
description: String(flow.description || ""),
|
|
12849
|
+
ownerUserId: owner.userId,
|
|
12850
|
+
ownerUsername: owner.username,
|
|
12851
|
+
adminReadonly: true,
|
|
12852
|
+
}))
|
|
12853
|
+
.sort((a, b) => Number(a.archived) - Number(b.archived) || a.id.localeCompare(b.id));
|
|
12854
|
+
json(res, 200, { owner, workspaces });
|
|
12855
|
+
} catch (e) {
|
|
12856
|
+
json(res, 500, { error: (e && e.message) || String(e) });
|
|
12857
|
+
}
|
|
12858
|
+
return;
|
|
12859
|
+
}
|
|
12860
|
+
|
|
12650
12861
|
if (req.method === "GET" && url.pathname === "/api/admin/run-detail") {
|
|
12651
12862
|
if (!authUser?.isAdmin) {
|
|
12652
12863
|
json(res, 403, { error: "Admin permission required" });
|
|
@@ -13022,12 +13233,17 @@ export function startUiServer({
|
|
|
13022
13233
|
flowId,
|
|
13023
13234
|
flowSource,
|
|
13024
13235
|
workspaceId: payload.workspaceId || "",
|
|
13236
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13025
13237
|
archived,
|
|
13026
13238
|
}, userCtx);
|
|
13027
13239
|
if (scoped.error) {
|
|
13028
13240
|
json(res, scoped.status || 400, { error: scoped.error });
|
|
13029
13241
|
return;
|
|
13030
13242
|
}
|
|
13243
|
+
if (scoped.adminReadonly) {
|
|
13244
|
+
json(res, 403, { error: "Admin Workspace review is read-only" });
|
|
13245
|
+
return;
|
|
13246
|
+
}
|
|
13031
13247
|
const ensured = ensureWorkspaceCollaboration({
|
|
13032
13248
|
flowId,
|
|
13033
13249
|
flowSource,
|
|
@@ -13139,7 +13355,12 @@ export function startUiServer({
|
|
|
13139
13355
|
json(res, scoped.status || 400, { error: scoped.error });
|
|
13140
13356
|
return;
|
|
13141
13357
|
}
|
|
13142
|
-
const key = workspaceCollaborationEventKey(
|
|
13358
|
+
const key = workspaceCollaborationEventKey(
|
|
13359
|
+
workspaceScopedUserContext(scoped, userCtx),
|
|
13360
|
+
scoped.flowSource,
|
|
13361
|
+
scoped.flowId,
|
|
13362
|
+
scoped.archived,
|
|
13363
|
+
);
|
|
13143
13364
|
let subscribers = workspaceCollaborationSubscribers.get(key);
|
|
13144
13365
|
if (!subscribers) {
|
|
13145
13366
|
subscribers = new Set();
|
|
@@ -13256,7 +13477,8 @@ export function startUiServer({
|
|
|
13256
13477
|
return;
|
|
13257
13478
|
}
|
|
13258
13479
|
const { path: graphPath, graph } = readWorkspaceGraph(scoped.root);
|
|
13259
|
-
const
|
|
13480
|
+
const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
|
|
13481
|
+
const hydratedGraph = hydrateWorkspaceGraphForRuntime(root, scoped, graph, scopedUserCtx);
|
|
13260
13482
|
const collaborationAccess = scoped.collaborationAccess || workspaceCollaborationAccess(null, userCtx.userId);
|
|
13261
13483
|
json(res, 200, {
|
|
13262
13484
|
ok: true,
|
|
@@ -13270,9 +13492,15 @@ export function startUiServer({
|
|
|
13270
13492
|
flowSource: scoped.flowSource,
|
|
13271
13493
|
archived: scoped.archived,
|
|
13272
13494
|
writable: !(scoped.archived || isReadonlyBuiltinFlowSource(scoped.flowSource))
|
|
13495
|
+
&& scoped.adminReadonly !== true
|
|
13273
13496
|
&& collaborationAccess.writable !== false,
|
|
13274
13497
|
collaboration: workspaceCollaborationSummaryWithUsers(scoped.collaboration, userCtx.userId),
|
|
13275
|
-
|
|
13498
|
+
adminReview: scoped.adminReadonly ? {
|
|
13499
|
+
readonly: true,
|
|
13500
|
+
ownerUserId: scoped.ownerUserId,
|
|
13501
|
+
ownerUsername: scoped.ownerUsername,
|
|
13502
|
+
} : null,
|
|
13503
|
+
workspaceSchedules: listWorkspaceScheduleStatusesForFlow(scopedUserCtx, scoped.flowSource || "user", scoped.flowId || ""),
|
|
13276
13504
|
});
|
|
13277
13505
|
} catch (e) {
|
|
13278
13506
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
@@ -13293,12 +13521,17 @@ export function startUiServer({
|
|
|
13293
13521
|
flowId: payload.flowId || "",
|
|
13294
13522
|
flowSource: payload.flowSource || "user",
|
|
13295
13523
|
workspaceId: payload.workspaceId || "",
|
|
13524
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13296
13525
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13297
13526
|
}, userCtx);
|
|
13298
13527
|
if (scoped.error) {
|
|
13299
13528
|
json(res, 400, { error: scoped.error });
|
|
13300
13529
|
return;
|
|
13301
13530
|
}
|
|
13531
|
+
if (scoped.adminReadonly) {
|
|
13532
|
+
json(res, 403, { error: "Admin Workspace review is read-only" });
|
|
13533
|
+
return;
|
|
13534
|
+
}
|
|
13302
13535
|
const { graph } = readWorkspaceGraph(scoped.root);
|
|
13303
13536
|
const nodeIds = normalizeDisplayShareNodeIds(payload.nodeIds, graph);
|
|
13304
13537
|
if (nodeIds.length === 0) {
|
|
@@ -13338,6 +13571,7 @@ export function startUiServer({
|
|
|
13338
13571
|
flowId: payload.flowId || "",
|
|
13339
13572
|
flowSource: payload.flowSource || "user",
|
|
13340
13573
|
workspaceId: payload.workspaceId || "",
|
|
13574
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13341
13575
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13342
13576
|
}, userCtx);
|
|
13343
13577
|
if (scoped.error) {
|
|
@@ -13451,8 +13685,21 @@ export function startUiServer({
|
|
|
13451
13685
|
json(res, 400, { error: "Missing flowId" });
|
|
13452
13686
|
return;
|
|
13453
13687
|
}
|
|
13688
|
+
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
13689
|
+
flowId,
|
|
13690
|
+
flowSource,
|
|
13691
|
+
archived: url.searchParams.get("archived") === "1",
|
|
13692
|
+
}, userCtx);
|
|
13693
|
+
if (scoped.error) {
|
|
13694
|
+
json(res, scoped.status || 400, { error: scoped.error });
|
|
13695
|
+
return;
|
|
13696
|
+
}
|
|
13454
13697
|
json(res, 200, {
|
|
13455
|
-
schedules: listWorkspaceScheduleStatusesForFlow(
|
|
13698
|
+
schedules: listWorkspaceScheduleStatusesForFlow(
|
|
13699
|
+
workspaceScopedUserContext(scoped, userCtx),
|
|
13700
|
+
flowSource,
|
|
13701
|
+
flowId,
|
|
13702
|
+
),
|
|
13456
13703
|
});
|
|
13457
13704
|
} catch (e) {
|
|
13458
13705
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
@@ -13473,12 +13720,17 @@ export function startUiServer({
|
|
|
13473
13720
|
flowId: payload.flowId || "",
|
|
13474
13721
|
flowSource: payload.flowSource || "user",
|
|
13475
13722
|
workspaceId: payload.workspaceId || "",
|
|
13723
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13476
13724
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13477
13725
|
}, userCtx);
|
|
13478
13726
|
if (scoped.error) {
|
|
13479
13727
|
json(res, 400, { error: scoped.error });
|
|
13480
13728
|
return;
|
|
13481
13729
|
}
|
|
13730
|
+
if (scoped.collaborationAccess?.runnable === false) {
|
|
13731
|
+
json(res, 403, { error: "Workspace run permission denied" });
|
|
13732
|
+
return;
|
|
13733
|
+
}
|
|
13482
13734
|
const flowId = String(payload.flowId || "").trim();
|
|
13483
13735
|
if (!flowId) {
|
|
13484
13736
|
json(res, 400, { error: "Missing flowId" });
|
|
@@ -13521,6 +13773,7 @@ export function startUiServer({
|
|
|
13521
13773
|
flowId: payload.flowId || "",
|
|
13522
13774
|
flowSource: payload.flowSource || "user",
|
|
13523
13775
|
workspaceId: payload.workspaceId || "",
|
|
13776
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13524
13777
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13525
13778
|
}, userCtx);
|
|
13526
13779
|
if (scoped.error) {
|
|
@@ -13585,6 +13838,7 @@ export function startUiServer({
|
|
|
13585
13838
|
flowId: payload.flowId || "",
|
|
13586
13839
|
flowSource: payload.flowSource || "user",
|
|
13587
13840
|
workspaceId: payload.workspaceId || "",
|
|
13841
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13588
13842
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13589
13843
|
}, userCtx);
|
|
13590
13844
|
if (scoped.error) {
|
|
@@ -13868,9 +14122,10 @@ export function startUiServer({
|
|
|
13868
14122
|
json(res, scoped.status || 400, { error: scoped.error });
|
|
13869
14123
|
return;
|
|
13870
14124
|
}
|
|
14125
|
+
const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
|
|
13871
14126
|
json(res, 200, {
|
|
13872
14127
|
runs: listWorkspaceRunLogs({
|
|
13873
|
-
userId: flowSource === "workspace" ? "" :
|
|
14128
|
+
userId: flowSource === "workspace" ? "" : scopedUserCtx.userId || "",
|
|
13874
14129
|
flowId,
|
|
13875
14130
|
flowSource,
|
|
13876
14131
|
scheduleNodeId,
|
|
@@ -13902,8 +14157,9 @@ export function startUiServer({
|
|
|
13902
14157
|
json(res, scoped.status || 400, { error: scoped.error });
|
|
13903
14158
|
return;
|
|
13904
14159
|
}
|
|
14160
|
+
const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
|
|
13905
14161
|
const run = listWorkspaceRunLogs({
|
|
13906
|
-
userId: flowSource === "workspace" ? "" :
|
|
14162
|
+
userId: flowSource === "workspace" ? "" : scopedUserCtx.userId || "",
|
|
13907
14163
|
flowId,
|
|
13908
14164
|
flowSource,
|
|
13909
14165
|
limit: 200,
|
|
@@ -13939,7 +14195,7 @@ export function startUiServer({
|
|
|
13939
14195
|
json(res, scoped.status || 400, { error: scoped.error });
|
|
13940
14196
|
return;
|
|
13941
14197
|
}
|
|
13942
|
-
const scopeKey = workspaceRunKey(userCtx, flowSource, flowId);
|
|
14198
|
+
const scopeKey = workspaceRunKey(workspaceScopedUserContext(scoped, userCtx), flowSource, flowId);
|
|
13943
14199
|
const entries = workspaceActiveRunsForScope(scopeKey).map(([, entry]) => entry);
|
|
13944
14200
|
const entry = entries[0] || null;
|
|
13945
14201
|
json(res, 200, {
|
|
@@ -13980,6 +14236,7 @@ export function startUiServer({
|
|
|
13980
14236
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
13981
14237
|
flowId,
|
|
13982
14238
|
flowSource,
|
|
14239
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
13983
14240
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
13984
14241
|
}, userCtx);
|
|
13985
14242
|
if (scoped.error) {
|
|
@@ -14124,6 +14381,7 @@ export function startUiServer({
|
|
|
14124
14381
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14125
14382
|
flowId: payload.flowId || "",
|
|
14126
14383
|
flowSource: payload.flowSource || "user",
|
|
14384
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14127
14385
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14128
14386
|
}, userCtx);
|
|
14129
14387
|
if (scoped.error) {
|
|
@@ -14185,6 +14443,7 @@ export function startUiServer({
|
|
|
14185
14443
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14186
14444
|
flowId: payload.flowId || "",
|
|
14187
14445
|
flowSource: payload.flowSource || "user",
|
|
14446
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14188
14447
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14189
14448
|
}, userCtx);
|
|
14190
14449
|
if (scoped.error) {
|
|
@@ -14259,6 +14518,7 @@ export function startUiServer({
|
|
|
14259
14518
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14260
14519
|
flowId: parsed.fields.flowId || "",
|
|
14261
14520
|
flowSource: parsed.fields.flowSource || "user",
|
|
14521
|
+
adminOwnerId: parsed.fields.adminOwnerId || "",
|
|
14262
14522
|
archived: parsed.fields.archived === "1" || parsed.fields.archived === "true" || parsed.fields.flowArchived === "true",
|
|
14263
14523
|
}, userCtx);
|
|
14264
14524
|
if (scoped.error) {
|
|
@@ -14304,6 +14564,7 @@ export function startUiServer({
|
|
|
14304
14564
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14305
14565
|
flowId: payload.flowId || "",
|
|
14306
14566
|
flowSource: payload.flowSource || "user",
|
|
14567
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14307
14568
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14308
14569
|
}, userCtx);
|
|
14309
14570
|
if (scoped.error) {
|
|
@@ -14344,6 +14605,7 @@ export function startUiServer({
|
|
|
14344
14605
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14345
14606
|
flowId: payload.flowId || "",
|
|
14346
14607
|
flowSource: payload.flowSource || "user",
|
|
14608
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14347
14609
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14348
14610
|
}, userCtx);
|
|
14349
14611
|
if (scoped.error) {
|
|
@@ -14394,6 +14656,7 @@ export function startUiServer({
|
|
|
14394
14656
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14395
14657
|
flowId: req.method === "POST" ? (payload.flowId || "") : (url.searchParams.get("flowId") || ""),
|
|
14396
14658
|
flowSource: req.method === "POST" ? (payload.flowSource || "user") : (url.searchParams.get("flowSource") || "user"),
|
|
14659
|
+
adminOwnerId: req.method === "POST" ? (payload.adminOwnerId || "") : "",
|
|
14397
14660
|
archived: req.method === "POST"
|
|
14398
14661
|
? (payload.archived === true || payload.flowArchived === true)
|
|
14399
14662
|
: (url.searchParams.get("archived") === "1" || url.searchParams.get("flowArchived") === "1"),
|
|
@@ -14435,6 +14698,7 @@ export function startUiServer({
|
|
|
14435
14698
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14436
14699
|
flowId: payload.flowId || "",
|
|
14437
14700
|
flowSource: payload.flowSource || "user",
|
|
14701
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14438
14702
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14439
14703
|
}, userCtx);
|
|
14440
14704
|
if (scoped.error) {
|
|
@@ -14525,6 +14789,7 @@ export function startUiServer({
|
|
|
14525
14789
|
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
14526
14790
|
flowId: payload.flowId || "",
|
|
14527
14791
|
flowSource: payload.flowSource || "user",
|
|
14792
|
+
adminOwnerId: payload.adminOwnerId || "",
|
|
14528
14793
|
archived: payload.archived === true || payload.flowArchived === true,
|
|
14529
14794
|
}, userCtx);
|
|
14530
14795
|
if (scoped.error) {
|
|
@@ -15168,9 +15433,18 @@ export function startUiServer({
|
|
|
15168
15433
|
}
|
|
15169
15434
|
const nodesArchived = url.searchParams.get("archived") === "1";
|
|
15170
15435
|
try {
|
|
15436
|
+
const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
|
|
15437
|
+
if (requestedContext.error) {
|
|
15438
|
+
json(res, requestedContext.status || 403, { error: requestedContext.error });
|
|
15439
|
+
return;
|
|
15440
|
+
}
|
|
15171
15441
|
const { setLanguage } = await import("./i18n.mjs");
|
|
15172
15442
|
setLanguage(lang);
|
|
15173
|
-
json(res, 200, listNodesJson(root, flowId || "", flowId ? flowSource : "", {
|
|
15443
|
+
json(res, 200, listNodesJson(root, flowId || "", flowId ? flowSource : "", {
|
|
15444
|
+
archived: nodesArchived,
|
|
15445
|
+
...requestedContext.userCtx,
|
|
15446
|
+
marketplaceScope,
|
|
15447
|
+
}));
|
|
15174
15448
|
} catch (e) {
|
|
15175
15449
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
15176
15450
|
}
|
|
@@ -15191,7 +15465,15 @@ export function startUiServer({
|
|
|
15191
15465
|
}
|
|
15192
15466
|
const archived = url.searchParams.get("archived") === "1";
|
|
15193
15467
|
try {
|
|
15194
|
-
const
|
|
15468
|
+
const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
|
|
15469
|
+
if (requestedContext.error) {
|
|
15470
|
+
json(res, requestedContext.status || 403, { error: requestedContext.error });
|
|
15471
|
+
return;
|
|
15472
|
+
}
|
|
15473
|
+
const detail = readNodeDetailJson(root, nodeId, flowId, flowId ? (flowSource || "user") : "", {
|
|
15474
|
+
archived,
|
|
15475
|
+
...requestedContext.userCtx,
|
|
15476
|
+
});
|
|
15195
15477
|
if (detail.error) {
|
|
15196
15478
|
json(res, 404, { error: detail.error });
|
|
15197
15479
|
return;
|
|
@@ -15438,7 +15720,12 @@ export function startUiServer({
|
|
|
15438
15720
|
json(res, collaborationDenied.status, { error: collaborationDenied.error });
|
|
15439
15721
|
return;
|
|
15440
15722
|
}
|
|
15441
|
-
const
|
|
15723
|
+
const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
|
|
15724
|
+
if (requestedContext.error) {
|
|
15725
|
+
json(res, requestedContext.status || 403, { error: requestedContext.error });
|
|
15726
|
+
return;
|
|
15727
|
+
}
|
|
15728
|
+
const result = readFlowJson(root, flowId, flowSource, { archived: flowArchived, ...requestedContext.userCtx });
|
|
15442
15729
|
if (result.error) {
|
|
15443
15730
|
json(res, 404, result);
|
|
15444
15731
|
return;
|